// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) Huawei Technologies Co., Ltd. 2023. All rights reserved.
 * Description: Dh backend page ops
 * Author: Huawei OS Kernel Lab
 * Create: Sat Aug 05 14:55:56 2023
 */
#include <linux/gfp.h>
#include <linux/page_ref.h>
#include <linux/mm.h>
#include <linux/page-flags.h>

#define PageExternalMem(__p) (page_zonenum(__p) == ZONE_EXTERNAL)
void *dh_backend_alloc_page(unsigned int order)
{
	return (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO | __GFP_NORETRY,
					order);
}

void dh_backend_free_page(void *ptr, unsigned int order)
{
	struct page *pg = virt_to_page(ptr);
	/*
	 * The transfs I/O framework uses dh_backend_alloc_page and dh_backend_free_page
	 * to apply for alloc and release the io_ring shared memory.
	 *
	 * In the transfs I/O framework, when pages in the SHM are used, the page
	 * information is cleared to ensure that the current I/O process is not affected
	 * by the previous I/O process. After the page I/O process is used, the page count
	 * is reduced to 0. It does not comply with the page count management of Linux.
	 * To solve this problem, the count of the page needs to be reset to 1 in SHM
	 * release phase.
	 *
	 * Note: Pages related to the transfs I/O framework have the ExternalMem flag.
	 */
	if (PageExternalMem(pg)) {
		(void)memset(pg, 0, sizeof(struct page));
		set_page_count(pg, 1);
#ifdef WANT_PAGE_VIRTUAL
		set_page_address(pg, ptr);
#endif
	}
	free_pages((unsigned long)ptr, order);
}
